﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Web;

namespace MvcRags.Core
{
    public static class LinqHelper
    {
        public static IEnumerable<T> WhereStartsWith<T>(this IQueryable<T> source, string field, string value)
        {
            return WhereCall(source, field, value, "StartsWith");
        }

        public static IEnumerable<T> WhereContains<T>(this IQueryable<T> source, string field, string value)
        {
            return WhereCall(source, field, value, "Contains");
        }

        public static IEnumerable<T> WhereEndsWith<T>(this IQueryable<T> source, string field, string value)
        {
            return WhereCall(source,field,value,"EndsWith");
        }

        //from: http://stackoverflow.com/questions/11204817/use-string-as-field-name-in-linq
        public static IEnumerable<T> WhereCall<T>(this IQueryable<T> source, string field, string value, string call)
        {
            var param = Expression.Parameter(typeof(T), "x");
            var predicate = Expression.Lambda<Func<T, bool>>(
                Expression.Call(
                    Expression.PropertyOrField(param, field),
                    call, null, Expression.Constant(value)
                ), param);

            return source.Where(predicate);
        }

        //from: http://stackoverflow.com/questions/307512/how-do-i-apply-orderby-on-an-iqueryable-using-a-string-column-name-within-a-gene
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string field, string command)
        {
            var type = typeof(T);
            var property = type.GetProperty(field);
            var parameter = Expression.Parameter(type, "p");
            var propertyAccess = Expression.MakeMemberAccess(parameter, property);
            var orderByExp = Expression.Lambda(propertyAccess, parameter);
            MethodCallExpression resultExp = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
            return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(resultExp);
        }

        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string orderField)
        {
            const string command = "OrderBy";
            return (IOrderedQueryable<T>)source.OrderBy<T>(orderField, command);
        }

        public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string orderField)
        {
            const string command = "OrderByDescending";
            return (IOrderedQueryable<T>)source.OrderBy<T>(orderField, command);
        }
    }
}